package com.gitee.liuzhi.design.structure;

import com.gitee.liuzhi.common.model.Person;
import com.google.common.collect.Lists;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author liuzhi
 * @Date 2023/8/27 19:05
 * @Version 1.0
 * @Description 过滤器模式 Filter Criteria Pattern
 * <p>
 * 过滤器模式: 它用于通过一系列条件来筛选对象的集合
 * 过滤器模式允许将多个过滤条件组合在一起 以便根据不同的需求过滤对象
 * <p>
 * 主要角色
 * 1. 过滤器接口 (Criteria): 定义用于筛选对象的方法
 * 2. 具体过滤器 (ConcreteCriteria): 实现过滤器接口, 根据不同的条件对对象进行筛选
 * 3. 过滤器管理器 (CriteriaFilter): 包含多个过滤器对象, 并根据不同的条件组合它们, 对对象进行筛选
 * 4. 客户端 (Client): 使用过滤器管理器来过滤对象集合, 并获取满足条件的对象
 */
public class FilterCriteriaPattern {


    public static void main(String[] args) {
        List<Person> persons = Lists.newArrayList();
        persons.add(new Person("Robert","Male", "Single"));
        persons.add(new Person("John","Male", "Married"));
        persons.add(new Person("Laura","Female", "Married"));
        persons.add(new Person("Diana","Female", "Single"));
        persons.add(new Person("Mike","Male", "Single"));
        persons.add(new Person("Bobby","Male", "Single"));

        Criteria male = new CriteriaMale();
        Criteria female = new CriteriaFemale();
        Criteria single = new CriteriaSingle();
        Criteria singleMale = new AndCriteria(single, male);
        Criteria singleOrFemale = new OrCriteria(single, female);

        System.out.println("Males: ");
        printPersons(male.meetCriteria(persons));

        System.out.println("\nFemales: ");
        printPersons(female.meetCriteria(persons));

        System.out.println("\nSingle Males: ");
        printPersons(singleMale.meetCriteria(persons));

        System.out.println("\nSingle Or Females: ");
        printPersons(singleOrFemale.meetCriteria(persons));
    }

    /**
     * 过滤器接口
     */
    public interface Criteria {

        /**
         * 定义用于筛选对象的方法
         *
         * @param personList source List<Person>
         * @return filter List<Person>
         */
        List<Person> meetCriteria(List<Person> personList);
    }

    /**
     * 具体过滤器 (过滤男性)
     */
    public static class CriteriaMale implements Criteria {

        @Override
        public List<Person> meetCriteria(List<Person> personList) {
            return personList.stream()
                    .filter(x -> x.getGender().equalsIgnoreCase("MALE"))
                    .collect(Collectors.toList());
        }
    }

    /**
     * 具体过滤器 (过滤女性)
     */
    public static class CriteriaFemale implements Criteria {

        @Override
        public List<Person> meetCriteria(List<Person> personList) {
            return personList.stream()
                    .filter(x -> x.getGender().equalsIgnoreCase("FEMALE"))
                    .collect(Collectors.toList());
        }
    }

    /**
     * 具体过滤器 (过滤未婚)
     */
    public static class CriteriaSingle implements Criteria {

        @Override
        public List<Person> meetCriteria(List<Person> personList) {
            return personList.stream()
                    .filter(x -> x.getMaritalStatus().equalsIgnoreCase("SINGLE"))
                    .collect(Collectors.toList());
        }
    }


    /**
     * 过滤器管理器 (and 条件)
     */
    public static class AndCriteria implements Criteria {

        private final Criteria criteria;
        private final Criteria otherCriteria;

        public AndCriteria(Criteria criteria, Criteria otherCriteria) {
            this.criteria = criteria;
            this.otherCriteria = otherCriteria;
        }

        @Override
        public List<Person> meetCriteria(List<Person> persons) {
            List<Person> firstCriteriaPersons = criteria.meetCriteria(persons);
            return otherCriteria.meetCriteria(firstCriteriaPersons);
        }
    }

    /**
     * 过滤器管理器 (or 条件)
     */
    public static class OrCriteria implements Criteria {

        private final Criteria criteria;
        private final Criteria otherCriteria;

        public OrCriteria(Criteria criteria, Criteria otherCriteria) {
            this.criteria = criteria;
            this.otherCriteria = otherCriteria;
        }

        @Override
        public List<Person> meetCriteria(List<Person> persons) {
            List<Person> firstCriteriaItems = criteria.meetCriteria(persons);
            List<Person> otherCriteriaItems = otherCriteria.meetCriteria(persons);

            for (Person person : otherCriteriaItems) {
                if (!firstCriteriaItems.contains(person)) {
                    firstCriteriaItems.add(person);
                }
            }
            return firstCriteriaItems;
        }
    }

    public static void printPersons(List<Person> persons){
        for (Person person : persons) {
            System.out.println("Person : [ Name : " + person.getName()
                    +", Gender : " + person.getGender()
                    +", Marital Status : " + person.getMaritalStatus()
                    +" ]");
        }
    }

}
